//
//  ChannelsSendingTester.cpp
//  GPUProcessingTesting
//
//  Created by kadlubek47 on 17.06.2013.
//  Copyright (c) 2013 kadlubek47. All rights reserved.
//

#include "ChannelsSendingTester.h"



#pragma mark Create, Destroy

const char ChannelsSendingTester::kernelPath_[] = "/Users/kadlubeczek47/programowanie/Praca Inzynierska/parallel-computing-sound-signals_svn_tmp/trunk/GPUProcessingTesting/GPUProcessingTesting/simply_kernels.cl";

ChannelsSendingTester::ChannelsSendingTester(): OpenCLProcessorBase((char*)kernelPath_) {
    createKernels();
}

ChannelsSendingTester::~ChannelsSendingTester() {
    if(!buffersIn_.empty()) {
        for(int i = 0; i < buffersIn_.size(); ++i ) {
            clReleaseMemObject(buffersIn_[i]);
            clReleaseMemObject(buffersOut_[i]);
        }
        buffersIn_.clear();
        buffersOut_.clear();
    }
    
    clReleaseKernel(inOutKernel_);
}

void ChannelsSendingTester::createKernels() {
    inOutKernel_ = clCreateKernel(program_, "in_out", &lastCommandStatus_);
}



#pragma mark manyChannel main_function

void ChannelsSendingTester::manyChannels(float** channels, unsigned int channelsNum, unsigned int channelLength, float** destiny, double* timeOfCallCommandsFun, double* timeOfFlushAndFinish) {
    Timer timer;
    
    //Calling commands functions
    timer.start();
    
    if( (channelsNum != channelsNum_) || (channelLength != channelLength_) ) {
        channelsNum_ = channelsNum;
        channelLength_ = channelLength;
        createBuffersForChannels();
    }
    
    loadBlocksOfChannelsIntoGPU(channels);
    executeKernel();
    getBlocksOfChannelsFromGPU(destiny);
    
    timer.stop();
    *timeOfCallCommandsFun += timer.getTime();
    //.
    
    //Flushing and Finishing
    timer.start();
    
    clFlush(cmdQueue_);
    clFinish(cmdQueue_);
    
    timer.stop();
    *timeOfFlushAndFinish += timer.getTime();
    //.
}



#pragma mark manyChannel subordinate_functions

void ChannelsSendingTester::createBuffersForChannels() {
    if(!buffersIn_.empty()) {
        for(int i = 0; i < buffersIn_.size(); ++i ) {
            clReleaseMemObject(buffersIn_[i]);
            clReleaseMemObject(buffersOut_[i]);
        }
        buffersIn_.clear();
        buffersOut_.clear();
    }
    
    for (int j = 0; j < 2; ++j) {
        for (int i = 0; i < channelsNum_; ++i) {
            
            cl_mem buffer = clCreateBuffer(
                                           context_,
                                           CL_MEM_READ_WRITE,
                                           channelLength_ * sizeof(float),
                                           NULL,
                                           &lastCommandStatus_);
            if(j==0)
                buffersIn_.push_back(buffer);
            if(j==1)
                buffersOut_.push_back(buffer);
        }
    }
}

void ChannelsSendingTester::loadBlocksOfChannelsIntoGPU(float** channels) {
    for (int i = 0; i < channelsNum_; ++i) {
        lastCommandStatus_ = clEnqueueWriteBuffer(
                                      cmdQueue_,
                                      buffersIn_[i],
                                      CL_FALSE,
                                      0,
                                      channelLength_ * sizeof(float),
                                      channels[i],
                                      0,
                                      NULL,
                                      NULL);
    }
}

void ChannelsSendingTester::executeKernel() {
    size_t globalWorkSize[1];
    globalWorkSize[0] = channelLength_;
    
    for (int i = 0; i < channelsNum_; ++i) {
        
        lastCommandStatus_  = clSetKernelArg(
                                 inOutKernel_,
                                 0,
                                 sizeof(cl_mem),
                                 &buffersIn_[i]);
        
        lastCommandStatus_ |= clSetKernelArg(
                                 inOutKernel_,
                                 1,
                                 sizeof(cl_mem),
                                 &buffersOut_[i]);
        
        lastCommandStatus_ = clEnqueueNDRangeKernel(
                                        cmdQueue_,
                                        inOutKernel_,
                                        1,
                                        NULL,
                                        globalWorkSize,
                                        NULL,
                                        0,
                                        NULL,
                                        NULL);
    }
    
}

void ChannelsSendingTester::getBlocksOfChannelsFromGPU (float** destiny) {
    for (int i = 0; i < channelsNum_; ++i) {
        lastCommandStatus_ = clEnqueueReadBuffer(
                                     cmdQueue_,
                                     buffersOut_[i],
                                     CL_FALSE,
                                     0,
                                     channelLength_ * sizeof(float),
                                     destiny[i],
                                     0,
                                     NULL,
                                     NULL);
    }
}